{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第10章 图像分类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在前面的章节中，我们学习了一系列图像处理算法。这些图像处理算法能够帮助我们提取图像中的我们所需的信息，为后续的计算机视觉任务打下基础。在这一章中，我们将进入计算机视觉的第二部分——图像内容理解。图像内容理解又被称为视觉识别，其目标是希望计算机对图像的识别结果与人类对图像的识别结果一致。所以视觉识别通常需要一个（机器）学习的过程。自从2021年深度学习崛起以后，深度学习基本统治了视觉识别的所有任务。所以在视觉识别这一部分，我们将主要介绍基于深度学习的视觉识别算法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 10.1 简介"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic4.zhimg.com/80/v2-9576cd5719a3a1f7168c2d8ff3c01bfb_1440w.webp\n",
    "\" width=600>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图10-1 图像分类示例。</div>\n",
    "</center>\n",
    "\n",
    "\n",
    "图像分类是视觉识别中的一个最基本的问题，对这个问题的研究也是研究其他视觉识别问题的基础。图像分类，顾名思义，就是根据图像中的物体或者场景，给图像打个标签。如图10-1所示，图像分类就是分别给这些图像打上“猫”、“狗”、“沙滩”的标签。用数学语言来描述图像分类的问题即是：定义图像空间$\\mathbb{I}$和预定义的类别集合$\\mathcal{C}$，给定数据集$\\mathcal{D} = \\{(\\mathbf{I}^{(n)},y^{(n)})\\}_{n=1}^N$，其中$\\mathbf{I}^{(n)}\\in \\mathbb{I}$是数据集中的第$n$张图像，$y^{(n)}\\in\\mathcal{C}$是其对应的标签；图像分类的任务是从$\\mathcal{D}$中学习得到一个从图像空间到类别集合的映射$f:\\mathbb{I} \\rightarrow \\mathcal{C}$，从而给定任意一张测试图像$\\mathbf{I}$，我们可以用学习得到的映射函数$f$预测其标签：$\\hat{y}=f(\\mathbf{I})$。通常这个学习过程需要建模条件概率$p(y|\\mathbf{I})$，则$\\hat{y}=f(\\mathbf{I})=\\arg\\max_{y\\in\\mathcal{C}}p(y|\\mathbf{I})$。一个通用的图像分类流程如图 10-2 所示。首先，我们从用于训练的图像中提取图像表征，并结合对应的标签信息训练一个图像分类的模型（分类器）。对于每一张测试图像，我们采用同样的表征提取过程得到其表征，并利用训练得到的分类器预测其所属类别。\n",
    "\n",
    "\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic1.zhimg.com/80/v2-a126bac75f410c394b7d1e063ed6c060_1440w.webp\n",
    "\" width=800>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图10-2 图像分类流程。</div>\n",
    "</center>\n",
    "\n",
    "\n",
    "\n",
    "由于机器学习中不同类型分类器的性能大同小异，由上图可知，图像表征的提取是图像分类中最关键的一步。图像表征的好坏，直接决定了图像分类性能的高低。传统图像表征提取算法主要基于手工设计的图像特征向量，如我们前面学习的SIFT特征就是一种最典型的手工设计的图像特征向量。传统图像表征中的集大成者是视觉词袋模型（Bag-of-Features）[1-4]，由Andrew Zisserman等人受文本检索领域的词袋模型（Bag-of-Words）启发而提出。该表征模型首先从图像中检测并提取出多个特征点，之后利用聚类算法对这些特征点的特征向量进行聚类得到“词典”，在此基础上通过构建好的词典对每张图像中的特征向量进行编码，从而构建每张图像的表征，即视觉词袋模型表征。最后，基于视觉词袋模型表征，训练图像分类器。可以看到，传统的图像表征提取与图像分类器的训练是分开进行的，两个部分的信息彼此之间无法进行交互影响，因此分类性能会受一定的限制。随着深度学习的崛起，通过深度卷积神经网络（CNN）联合图像表征学习和分类器学习逐渐成为了主流，因为通过联合学习，能够大幅度提升图像分类的性能。第一个深度卷积神经网络由Yan Lecun于1989年提出 [5]，用于手写字符的识别。随后在1998年，他改进了这个网络。改进后的网络被称为LeNet-5 [6]，已具有所有现代深度网络的元素。2012年，Geffory Hinton带领他的学生构建了 AlexNet [7]模型，是一个8层的卷积神经网络，在ImageNet图像分类比赛中取得冠军，从此开启了图像分类的深度学习时代。2015年，微软亚洲研究院的研究员何凯明等人提出了ResNet [8]，是一种可以堆叠上千层的卷积神经网络，再度刷新了ImageNet图像分类比赛的最好成绩，从此深度学习开始统治整个视觉识别领域。\n",
    "\n",
    "接下来，我们将分别介绍基于传统的视觉词袋模型的图像分类算法和基于深度卷积网络ResNet的图像分类算法。在介绍算法之前，我们需要先了解一些常用于图像分类的数据集以及评测指标（metric）。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 10.2 数据集与评测指标"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在图像分类任务中，我们通常会选择以下几种常用的数据集进行模型的训练与测试：\n",
    "1. MNIST [9]：MNIST是一个手写数字的数据集，每一个样本都是一张黑白图像，其对应的标签为数字0-9中的一个。它包含了60000张训练图像和10000张测试图像。\n",
    "2. Caltech-101 [10]：Caltech-101于2004年由李飞飞等人提出。该数据集包含连同背景在内共102个类别9144张图像用于训练和测试。\n",
    "3. CIFAR-10 [11]:  CIFAR-10数据集是由 Geffory Hinton 的学生Alex Krizhevsky和Ilya Sutskever整理的一个用于识别常见类别的小型数据集，它一共包括50000张训练图像和10000张测试图像。\n",
    "4. ImageNet [12]： ImageNet是一个大规模图像识别数据库，由李飞飞团队从2007年开始，耗费大量人力，通过各种方式收集制作而成。ImageNet数据集一共包含了14197122张图像，共1000个类别。\n",
    "\n",
    "这里，我们使用Caltech-101作为我们后续实验的数据集。我们随机选择其中的60%作为训练样本，剩下的20%作为验证样本，20%作为测试样本。\n",
    "\n",
    "在图像分类中，我们通常选择准确率作为评测指标。对于$n$张测试图像样本，若模型预测类别准确的样本数为$m$，则该模型的准确率为$\\frac{m}{n}\\times 100\\%$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 10.3 词袋模型"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "词袋模型（Bag of Features，BoF）是一种提取图像表征的方法，它可大致分为以下四步：\n",
    "1. 从训练图像集中提取特征，形成特征库；\n",
    "2. 对特征库中的特征进行聚类，构建“视觉词典”；\n",
    "3. 用所学到的视觉词典对每张图像的特征进行编码；\n",
    "4. 基于特征编码对图像进行表征。\n",
    "\n",
    "\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic4.zhimg.com/80/v2-3276fc1c87f8b59342e3cdf5e8ac130f_1440w.webp\n",
    "\" width=400>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图10-3 对特征聚类形成视觉词典。</div>\n",
    "</center>\n",
    "\n",
    "具体地，首先，从给定的训练图像中提取特征：通常，我们可以提取图像的SIFT特征。根据我们在第7章所学的内容，每个SIFT特征描述符是一个128维的向量。将从训练图像集提取的所有SIFT特征收集到一起，构建一个图像特征库。接着，我们通过聚类，如Kmeans聚类，将这些图像特征聚成$K$个聚类中心。这些聚类中心我们称之为“视觉单词”，所有视觉单词构成的集合即为视觉词典。如图 10-3 所示，图中的每一小块表示一个视觉单词。在此之后，我们需要利用视觉词典对图像特征进行编码。举一个最简单的编码例子：对图像中的每一个特征，我们都可以在词典中找到一个与其最相似的视觉单词来表示这一特征，我们称这个过程为将该特征分配给（assign）这个与之最相似的视觉单词。一个视觉单词被分配的次数越多，也就可以认为这个视觉单词表示的语义在图像中出现得越多。所以，如图 10-4 所示，我们可以用一个直方图来统计一张图像中的视觉单词出现的频率，并最终使用这一信息来形成图像的表征。\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic4.zhimg.com/80/v2-a2787f1c304f8ee4665314f3ee02946b_1440w.jpg\n",
    "\" width=600>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图10-4 基于视觉词典构建图像表征。</div>\n",
    "</center>\n",
    "\n",
    "最终，我们得到所有训练图像的表征。基于图像的表征，我们再训练一个常见的多分类器如SVM等，便可以实现图像分类。接下来，我们将动手编写实现基于视觉词袋模型的图像分类算法。我们先导入必要的库，并导入Caltech-101数据集。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from PIL import Image\n",
    "import cv2\n",
    "import numpy as np\n",
    "import os\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.kernel_approximation import AdditiveChi2Sampler\n",
    "import random\n",
    "import math\n",
    "from imutils import paths"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-11-17T08:19:58.800786Z",
     "iopub.status.busy": "2022-11-17T08:19:58.799615Z",
     "iopub.status.idle": "2022-11-17T08:21:11.642963Z",
     "shell.execute_reply": "2022-11-17T08:21:11.641717Z",
     "shell.execute_reply.started": "2022-11-17T08:19:58.800689Z"
    }
   },
   "outputs": [],
   "source": [
    "# 图像数据\n",
    "data = []\n",
    "# 图像对应的标签\n",
    "labels = []\n",
    "# 储存标签信息的临时变量\n",
    "labels_tep = []\n",
    "# 数据集的地址\n",
    "image_paths = list(paths.list_images('./caltech-101'))\n",
    "\n",
    "for image_path in image_paths:\n",
    "    # 获取图像类别\n",
    "    label = image_path.split(os.path.sep)[-2]\n",
    "    # 读取每个类别的图像\n",
    "    image = cv2.imread(image_path)\n",
    "    # 将图像通道从BGR转换为RGB\n",
    "    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n",
    "    # 统一输入图像的尺寸\n",
    "    image = cv2.resize(image, (200,200), interpolation=cv2.INTER_AREA)\n",
    "    data.append(image)\n",
    "    labels_tep.append(label)\n",
    "\n",
    "name2label = {}\n",
    "tep = {}\n",
    "for idx, name in enumerate(labels_tep):\n",
    "    tep[name] = idx\n",
    "for idx, name in enumerate(tep):\n",
    "    name2label[name] = idx\n",
    "for idx, image_path in enumerate(image_paths):\n",
    "    labels.append(name2label[image_path.split(os.path.sep)[-2]])\n",
    "\n",
    "data = np.array(data)\n",
    "labels = np.array(labels)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们接着对数据集进行训练集验证集和测试集的划分。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x_train examples: (5486, 200, 200, 3)\n",
      "x_test examples: (1829, 200, 200, 3)\n",
      "x_val examples: (1829, 200, 200, 3)\n"
     ]
    }
   ],
   "source": [
    "(x_train, X, y_train, Y) = train_test_split(data, labels, test_size=0.4, stratify=labels, random_state=42)\n",
    "(x_val, x_test, y_val, y_test) = train_test_split(X, Y, test_size=0.5, random_state=42)\n",
    "print(f\"x_train examples: {x_train.shape}\\nx_test examples: {x_test.shape}\\nx_val examples: {x_val.shape}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-11-14T04:15:42.707808Z",
     "iopub.status.busy": "2022-11-14T04:15:42.706668Z",
     "iopub.status.idle": "2022-11-14T04:15:42.717775Z",
     "shell.execute_reply": "2022-11-14T04:15:42.716426Z",
     "shell.execute_reply.started": "2022-11-14T04:15:42.707764Z"
    }
   },
   "source": [
    "我们先进行视觉词典的构建。为了得到视觉词典，我们首先需要对输入图像的表征进行提取，这里我们使用SIFT对图像进行处理。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 构建一个词典，储存每一个类别的sift信息\n",
    "vec_dict = {i:{'kp':[], 'des':{}} for i in range(102)}\n",
    "\n",
    "sift = cv2.SIFT_create()\n",
    "for i in range(x_train.shape[0]):\n",
    "    # 对图像正则化\n",
    "    tep = cv2.normalize(x_train[i], None, 0, 255, cv2.NORM_MINMAX).astype('uint8')\n",
    "    # 计算图像的SIFT特征\n",
    "    kp_vector, des_vector = sift.detectAndCompute(tep, None)\n",
    "    # keypoint和descriptor信息储存进词典中\n",
    "    vec_dict[y_train[i]]['kp'] += list(kp_vector)\n",
    "    for k in range(len(kp_vector)):\n",
    "        # des使用kp_vector将其一一对应\n",
    "        vec_dict[y_train[i]]['des'][kp_vector[k]] = des_vector[k]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接着，为了保持类别之间的平衡，我们为每个类别选取相同个数的特征用于之后的聚类。我们先统计每个类别的SIFT图像特征个数，找到拥有最少特征个数的类别，然后以该类别的特征个数为标准个数，从每个类别的特征中筛选出标准个数的特征进行后续的聚类任务。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-11-16T07:30:27.467701Z",
     "iopub.status.busy": "2022-11-16T07:30:27.467387Z",
     "iopub.status.idle": "2022-11-16T07:30:56.448159Z",
     "shell.execute_reply": "2022-11-16T07:30:56.446862Z",
     "shell.execute_reply.started": "2022-11-16T07:30:27.467673Z"
    }
   },
   "outputs": [],
   "source": [
    "# 设置最少特征点的数目\n",
    "bneck_value = float(\"inf\")\n",
    "\n",
    "# 确定bneck_value的数值\n",
    "for i in range(102):\n",
    "    if len(vec_dict[i]['kp']) < bneck_value:\n",
    "        bneck_value = len(vec_dict[i]['kp'])\n",
    "\n",
    "# 按照每一个SIFT特征点的响应值对特征进行降序排序\n",
    "for i in range(102):\n",
    "    kp_list = vec_dict[i]['kp'] = sorted((vec_dict[i]['kp']), key=lambda x: x.response, reverse=True)\n",
    "    \n",
    "    \n",
    "        \n",
    "# 为每个类别选择同样多的特征点用于聚类。特征点个数bneck_value\n",
    "vec_list=[]\n",
    "for i in range(bneck_value):\n",
    "    vec_list.append(vec_dict[0]['des'][vec_dict[0]['kp'][i]])\n",
    "\n",
    "for i in range(1, 102):\n",
    "    for j in range(bneck_value):\n",
    "        vec_list.append(vec_dict[i]['des'][vec_dict[i]['kp'][j]])\n",
    "\n",
    "vec_list = np.float64(vec_list)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "然后，我们使用Kmeans聚类算法将这些特征进行聚类，得到的聚类中心组成视觉词典。这里，我们将聚类中心个数设置为200。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-11-16T07:30:59.703093Z",
     "iopub.status.busy": "2022-11-16T07:30:59.702575Z",
     "iopub.status.idle": "2022-11-16T07:57:08.852213Z",
     "shell.execute_reply": "2022-11-16T07:57:08.851048Z",
     "shell.execute_reply.started": "2022-11-16T07:30:59.703040Z"
    }
   },
   "outputs": [],
   "source": [
    "from sklearn.cluster import KMeans\n",
    "N_clusters = 200\n",
    "kmeans = KMeans(n_clusters=N_clusters, random_state=0).fit(vec_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在此基础上，我们利用视觉词典对图像中的特征进行编码，并利用直方图统计编码的结果，最后将直方图归一化后便得到图像的表征。注意到这种基于整张图像特征编码的直方图统计表征会丢失图像中特征的位置信息，即把这些特征的位置任意地改变，其词袋模型表征是不变的。为了克服这个问题，我们引入空间金字塔匹配（Spatial Pyramid Matching，SPM）[1]算法提升词袋模型的表征能力。空间金字塔匹配对图像进行不同层次的划分。如图 10-5 所示，在金字塔的不同层次中，图像被划分成的块数不同，分别为$1\\times1$、$2\\times2$、$4\\times4$。我们对每一层中划分的图像块都通过上述词袋模型构建其直方图表征，最后将所有层次的图像块的直方图连接起来组成一个向量，即是整个图像的表征。\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic2.zhimg.com/80/v2-a9acf5e11663843f15015a1734c427e9_1440w.webp\n",
    "\" width=500>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图10-5 空间金字塔匹配对图像进行划分。</div>\n",
    "</center>\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-11-16T07:57:08.854128Z",
     "iopub.status.busy": "2022-11-16T07:57:08.853790Z",
     "iopub.status.idle": "2022-11-16T08:08:49.010003Z",
     "shell.execute_reply": "2022-11-16T08:08:49.008139Z",
     "shell.execute_reply.started": "2022-11-16T07:57:08.854099Z"
    }
   },
   "outputs": [],
   "source": [
    "def extract_SIFT(img):\n",
    "    sift = cv2.SIFT_create()\n",
    "    descriptors = []\n",
    "    for disft_step_size in DSIFT_STEP_SIZE:\n",
    "        keypoints = [cv2.KeyPoint(x, y, disft_step_size)\n",
    "                for y in range(0, img.shape[0], disft_step_size)\n",
    "                    for x in range(0, img.shape[1], disft_step_size)]\n",
    "\n",
    "        descriptors.append(sift.compute(img, keypoints)[1])\n",
    "    \n",
    "    return np.concatenate(descriptors, axis=0).astype('float64')\n",
    "\n",
    "\n",
    "# 获取图像的SPM特征\n",
    "def getImageFeaturesSPM(L, img, kmeans, k):\n",
    "    W = img.shape[1]\n",
    "    H = img.shape[0]   \n",
    "    h = []\n",
    "    for l in range(L+1):\n",
    "        w_step = math.floor(W/(2**l))\n",
    "        h_step = math.floor(H/(2**l))\n",
    "        x, y = 0, 0\n",
    "        for _ in range(2**l):\n",
    "            x = 0\n",
    "            for __ in range(2**l):\n",
    "                desc = extract_SIFT(img[y:y+h_step, x:x+w_step])                \n",
    "                predict = kmeans.predict(desc)\n",
    "                histo = np.bincount(predict, minlength=k).reshape(1,-1).ravel()\n",
    "                weight = 2**(l-L)\n",
    "                h.append(weight*histo)\n",
    "                x = x + w_step\n",
    "            y = y + h_step\n",
    "            \n",
    "    hist = np.array(h).ravel()\n",
    "    hist /= np.sum(hist)\n",
    "    return hist\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们分别使用SPM提取训练集和测试集图像的特征。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# SPM 的一些超参数\n",
    "pyramid_level = 2\n",
    "DSIFT_STEP_SIZE = [4, 8]\n",
    "\n",
    "hist_vector = []\n",
    "for i in range(x_train.shape[0]):\n",
    "    tep = cv2.normalize(x_train[i], None, 0, 255, cv2.NORM_MINMAX).astype('uint8')\n",
    "    # 提取图像的SPM特征\n",
    "    hist_SPM = getImageFeaturesSPM(pyramid_level, tep, kmeans, N_clusters)\n",
    "    # 将提取的特征加入直方图中\n",
    "    hist_vector.append(hist_SPM)\n",
    "hist_vector = np.array(hist_vector)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "hist_test_vector = []\n",
    "for i in range(x_test.shape[0]):\n",
    "    tep = cv2.normalize(x_test[i], None, 0, 255, cv2.NORM_MINMAX).astype('uint8')\n",
    "    hist_SPM = getImageFeaturesSPM(pyramid_level, tep, kmeans, N_clusters)\n",
    "    hist_test_vector.append(hist_SPM)\n",
    "hist_test_vector = np.array(hist_test_vector)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，我们训练一个SVM分类器，进行图像分类。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-11-16T08:08:49.012878Z",
     "iopub.status.busy": "2022-11-16T08:08:49.012359Z",
     "iopub.status.idle": "2022-11-16T08:09:55.099782Z",
     "shell.execute_reply": "2022-11-16T08:09:55.098643Z",
     "shell.execute_reply.started": "2022-11-16T08:08:49.012825Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "LinearSVC()"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from sklearn import svm\n",
    "\n",
    "# transform使用一种特征映射的方法\n",
    "transformer = AdditiveChi2Sampler()\n",
    "transformer = transformer.fit(np.concatenate([hist_vector, hist_test_vector], axis=0))\n",
    "\n",
    "# 构建SVM分类器\n",
    "classifier = svm.LinearSVC()\n",
    "# 将训练的直方图进行特征映射\n",
    "hist_vector = transformer.transform(hist_vector)\n",
    "\n",
    "# 对数据进行拟合\n",
    "classifier.fit(hist_vector, y_train)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，我们在测试集上对分类器进行评估，并计算分类准确率。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2022-11-16T08:09:55.101448Z",
     "iopub.status.busy": "2022-11-16T08:09:55.101110Z",
     "iopub.status.idle": "2022-11-16T08:14:55.571032Z",
     "shell.execute_reply": "2022-11-16T08:14:55.570019Z",
     "shell.execute_reply.started": "2022-11-16T08:09:55.101418Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "accuracy 0.6867140513942045\n"
     ]
    }
   ],
   "source": [
    "# 将测试的直方图进行特征映射\n",
    "hist_test_vector = transformer.transform(hist_test_vector)\n",
    "\n",
    "# 计算分类准确率\n",
    "acc = classifier.predict(hist_test_vector)-y_test\n",
    "tep = len(acc[acc==0])\n",
    "print('accuracy', tep/len(y_test))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以发现，利用词袋模型在Caltech-101数据集上图像分类的准确率达到了60%以上。当然，我们也可以通过其他的方式在现有基础上提高模型的准确率，我们可以使用Dense SIFT [13-14] 替换SIFT从图像中提取特征。Dense SIFT算法是对SIFT算法的改进版本，它先对输入图像进行分块处理，再对每一块进行SIFT运算并进行特征提取，感兴趣的同学可以查阅参考文献的论文，这里不做详细说明。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 10.4 基于深度学习的图像分类"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2012年，随着AlexNet的横空出世，基于深度卷积神经网络的方法逐渐成为计算机视觉的主流。对于图像分类这个任务而言，利用深度卷积神经网络可以实现从图像到分类标签的端到端预测，不再需要分阶段的表征提取与分类器训练。\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic4.zhimg.com/80/v2-8d618deef73410777f9b508052801a1b_1440w.webp\n",
    "\" width=400>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图10-6 Resnet残差结构。</div>\n",
    "</center>\n",
    "\n",
    "ResNet网络是在2015年由微软实验室的研究院何凯明等人提出，斩获当年ImageNet竞赛中分类任务第一名，目标检测第一名。在ResNet提出之前，所有的神经网络都是通过卷积层和池化层的叠加组成的（关于神经网络的相关知识，请读者参阅《动手学深度学习》）。人们认为卷积层和池化层的层数越多，获取到的图像表征信息越强，学习效果也就越好。但是在实际的实验中学者们发现，随着卷积层和池化层的叠加，不但没有出现学习效果越来越好的情况，反而出现了梯度消失和梯度爆炸等问题。为了解决上述问题，ResNet提出通过数据的预处理以及在网络中使用BN（Batch Normalization）层，同时，可以人为地让神经网络某些层跳过下一层神经元的连接，隔层相连，弱化每层之间的强联系，如图 10-6 所示，这样的网络叫做残差网络（residual network），这也是Resnet名称的由来。Resnet的结构如图 10-7 所示。\n",
    "\n",
    "\n",
    "<center>\n",
    "    <img style=\"border-radius: 0.3125em;\" \n",
    "    src=\"https://pic2.zhimg.com/80/v2-b6478731107b1ed198b4b3ad6fd13aa1_1440w.jpg\n",
    "\" width=800>\n",
    "    <br>\n",
    "    <div style=\"color:orange; \n",
    "    display: inline-block;\n",
    "    color: #999;\n",
    "    padding: 2px;\">图10-7 Resnet网络结构。</div>\n",
    "</center>\n",
    "\n",
    "我们以使用ResNet18进行分类为例进一步说明ResNet的结构。我们将一张分辨率大小为 $224\\times 224$ 的RGB图像输入进ResNet，首先ResNet通过conv_1层网络将其进行卷积得到一个分辨率大小为 $112\\times 112$，通道数为64的“特征图”。通道数是神经网络中的一个非常重要的概念。我们知道一张图像可以分解成R、G、B三个通道，每一个通道都蕴涵不同的信息。在深度学习中，我们往往通过提升图像的通道数，让其变成一张拥有多个通道的特征图。同RGB通道一样，特征图的每个通道都蕴涵着不同的信息，我们可以通过这种方式极大地丰富一张图像蕴含的信息。那么，我们该如何改变特征图的通道数呢？方法很简单，我们仍然可以通过卷积来完成。对于一个通道数为 $t$ 的特征图，可以使用 $t$ 个大小为 $k\\times k$ 的卷积核同时对特征图进行卷积，这样便可以得到 $t$ 个不同的卷积结果，再将这 $t$ 个结果相加可以得到最终的一个输出。这里可以将卷积核的大小视作为 $t\\times k\\times k$。接着，可以使用 $n$ 个大小为 $t\\times k\\times k$ 的卷积核对特征图进行卷积，便可得到 $n$ 个输出，这 $n$ 个输出便是一个通道数为 $n$ 的特征图（关于多通道特征图卷积的详细内容请参阅《动手学深度学习》）。在此之后，ResNet通过4个模块，每一次将特征图的大小依次进行两倍的压缩，最终conv_5的输出是一个512通道的 $7\\times 7$ 的特征图。在此之后，我们将特征图依次输入进池化和全连接层。全连接层将一个任意通道数的特征图映射到一个维度为类别数目的向量，该向量表示模型预测的类别的概率分布。最终，我们便可以根据这一概率分布，预测图像的类别。\n",
    "\n",
    "为了更深入的了解ResNet，我们先动手编写ResNet34网络。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch as t\n",
    "from torch import nn\n",
    "from torch.nn import functional as F\n",
    "\n",
    "# 残差结构\n",
    "class ResidualBlock(nn.Module):\n",
    "    \n",
    "    # 深度学习中的图像和利用模型提取的特征图往往有很多“通道”（channel），比如RGB图像的通道为3，即R通道、B通道与G通道\n",
    "    def __init__(self, inchannel, outchannel, stride=1, shortcut=None):\n",
    "        super(ResidualBlock, self).__init__()\n",
    "        # 观察图 10-6，我们发现残差结构可大致分为左右两部分，左边是一系列的网络层级，右边是一个跳跃连接\n",
    "        # 定义左边\n",
    "        self.left = nn.Sequential(\n",
    "            # 对应图 10-6 中第一个 weight layer\n",
    "            nn.Conv2d(inchannel, outchannel, 3, stride, 1, bias=False),\n",
    "            nn.BatchNorm2d(outchannel),\n",
    "            \n",
    "            # 对应图 10-6 中的 relu\n",
    "            nn.ReLU(inplace=True),\n",
    "            \n",
    "            # 对应图 10-6 中第二个 weight layer\n",
    "            nn.Conv2d(outchannel, outchannel, 3, 1, 1, bias=False),\n",
    "            nn.BatchNorm2d(outchannel))\n",
    "        \n",
    "        # 定义右边\n",
    "        self.right = shortcut\n",
    "        \n",
    "    # forward() 函数在网络结构中起到举足轻重的作用，它决定着网络如何对数据进行传播\n",
    "    def forward(self, x):\n",
    "        out = self.left(x)\n",
    "        # 构建残差结构\n",
    "        residual = x if self.right is None else self.right(x)\n",
    "        out += residual\n",
    "        \n",
    "        return F.relu(out)\n",
    "\n",
    "\n",
    "# 在这一模块中，我们将实现 ResNet34，其对应的结构可查看图 10-7\n",
    "class ResNet(nn.Module):\n",
    "    def __init__(self, num_classes=102):\n",
    "        super(ResNet, self).__init__()\n",
    "        # 前几层图像转换\n",
    "        self.pre = nn.Sequential(\n",
    "            nn.Conv2d(3, 64, 7, 2, 3, bias=False),\n",
    "            nn.BatchNorm2d(64),\n",
    "            nn.ReLU(inplace=True),\n",
    "            nn.MaxPool2d(3, 2, 1),\n",
    "        )\n",
    "\n",
    "        # 重复的 layer 分别有 3，4，6，3 个 residual block\n",
    "        self.layer1 = self._make_layer(64, 128, 3)\n",
    "        self.layer2 = self._make_layer(128, 256, 4, stride=2)\n",
    "        self.layer3 = self._make_layer(256, 512, 6, stride=2)\n",
    "        self.layer4 = self._make_layer(512, 512, 3, stride=2)\n",
    "\n",
    "        # 分类用的全连接层，将一个多通道的特征图映射到一个维度为类别数目的向量\n",
    "        self.fc = nn.Linear(512, num_classes)\n",
    "\n",
    "    def _make_layer(self, inchannel, outchannel, block_num, stride=1):\n",
    "        # 定义shortcut连接\n",
    "        shortcut = nn.Sequential(\n",
    "            nn.Conv2d(inchannel, outchannel, 1, stride, bias=False),\n",
    "            nn.BatchNorm2d(outchannel))\n",
    "\n",
    "        layers = []\n",
    "        \n",
    "        # 给当前网络层级添加残差块\n",
    "        layers.append(ResidualBlock(inchannel, outchannel, stride, shortcut))\n",
    "\n",
    "        for i in range(1, block_num):\n",
    "            layers.append(ResidualBlock(outchannel, outchannel))\n",
    "\n",
    "        return nn.Sequential(*layers)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.pre(x)\n",
    "\n",
    "        x = self.layer1(x)\n",
    "        x = self.layer2(x)\n",
    "        x = self.layer3(x)\n",
    "        x = self.layer4(x)\n",
    "\n",
    "        x = F.avg_pool2d(x, 7)\n",
    "        x = x.view(x.size(0), -1)\n",
    "\n",
    "        return self.fc(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们可以使用nn.Covn2d()函数实现卷积操作，该函数定义如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nn.Conv2d(in_channels,          # 输入的通道数\n",
    "          out_channels,         # 输出的通道数\n",
    "          kernel_size,          # 卷积核的大小\n",
    "          stride=1,             # 卷积核移动的步长\n",
    "          padding=0,            # 卷积核补零的层数\n",
    "          )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，我们将动手编写完成基于Resnet34的图像分类任务。首先，我们先导入必要的库。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.optim as optim\n",
    "import torch\n",
    "import torch.optim\n",
    "import torch.utils.data as Data\n",
    "import torchvision.transforms as transforms\n",
    "import torchvision.datasets as datasets\n",
    "import torchvision.models\n",
    "from torch.utils.data import DataLoader, Dataset\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "一般而言，在进行深度学习时，为了让深度网络学习的信息更加鲁棒，我们都会对图像进行“数据增强”。简单来说，数据增强是指在数据量比较固定的情况下，通过对原有的数据进行灰度、裁切、旋转、镜像、明度、色调、饱和度变化的一系列过程，用来增加数据量，以提高神经网络的性能。在我们的实验中我们也对图像数据行了增强。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义训练图像增强（变换）的方法\n",
    "train_transform = transforms.Compose(\n",
    "    [transforms.ToPILImage(),\n",
    "     # transforms.Resize((224, 224)),\n",
    "     transforms.ToTensor(),\n",
    "     transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])\n",
    "\n",
    "# 定义训练图像增强（变换）的方法\n",
    "val_transform = transforms.Compose(\n",
    "    [transforms.ToPILImage(),\n",
    "     # transforms.Resize((224, 224)),\n",
    "     transforms.ToTensor(),\n",
    "     transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接着，我们把Caltech101数据集封装成类，并生成训练集类、验证集类以及测试集类。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 数据集\n",
    "class ImageDataset(Dataset):\n",
    "    def __init__(self, images, labels=None, transforms=None):\n",
    "        self.X = images\n",
    "        self.y = labels\n",
    "        self.transforms = transforms\n",
    "\n",
    "    def __len__(self):\n",
    "        return (len(self.X))\n",
    "    \n",
    "    # 用于深度学习训练构建的数据类中，__getitem__()函数非常重要，它决定着数据如何传入模型\n",
    "    # 在下面的代码中，我们可以发现，当transform非空时，数据将先经过transform进行数据增强，再返回进行后续操作\n",
    "    def __getitem__(self, i):\n",
    "        data = self.X[i][:]\n",
    "\n",
    "        if self.transforms:\n",
    "            data = self.transforms(data)\n",
    "\n",
    "        if self.y is not None:\n",
    "            return (data, self.y[i])\n",
    "        else:\n",
    "            return data\n",
    "\n",
    "# 生成不同的类用于训练、验证以及测试\n",
    "train_data = ImageDataset(x_train, y_train, train_transform)\n",
    "val_data = ImageDataset(x_val, y_val, val_transform)\n",
    "test_data = ImageDataset(x_test, y_test, val_transform)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们将生成的类传入数据加载器中。通常情况下，在训练深度网络时我们一般选择使用数据加载器将数据传入网络中。数据加载器非常像一个迭代器，每次返回batch_size张图像与对应的标签。这里你有可能会问，为什么不一次返回所有的图像呢？batch_size又是什么呢？\n",
    "\n",
    "batch_size表示单次传递给程序用以训练的数据个数。比如我们的训练集有5486张训练图像，设置batch_size=100，那么模型首先会调用数据集中的前100个训练样本，即第1-100个数据来训练模型。当训练完成之后，再使用第101-200的个数据训练，直至数据集中的数据全部输入进模型为止。一般情况下，由于GPU资源以及内存的限制，我们无法将数据集中所有的图像数据输入进模型进行训练，因此我们需要设置一个参数batch_size，每次传入一部分数据进行训练，这样便可以减少内存的使用。同时这样做也可以提高训练的速度，因为每次完成训练后模型都会变得更加精确。\n",
    "\n",
    "在我们的实验中，我们设置batch_size=2048。如果你的计算资源不充分，你可以将其调小一些。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "BATCH_SIZE = 2048\n",
    "\n",
    "trainloader = DataLoader(train_data, batch_size=BATCH_SIZE, shuffle=True)\n",
    "valloader = DataLoader(val_data, batch_size=BATCH_SIZE, shuffle=True)\n",
    "testloader = DataLoader(test_data, batch_size=BATCH_SIZE, shuffle=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "接下来，我们加载开始加载ResNet34模型。这里我们将直接调用已经封装好的库。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 根据当前设备选择使用cpu或者gpu训练\n",
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "\n",
    "\n",
    "# 加载ImageNet预训练的模型\n",
    "model = torchvision.models.resnet34(pretrained='imagenet')\n",
    "num_ftrs = model.fc.in_features\n",
    "model.fc = nn.Linear(num_ftrs, 102)\n",
    "model.to(device)\n",
    "\n",
    "# 多张GPU并行训练\n",
    "model = nn.DataParallel(model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们注意到上述代码中出现了“pretrained”的参数，这是神经网络中的一个重要操作“预训练”。大多数情况下，我们能够用于训练模型的算力和数据都很有限，因此我们希望能够尽量重复利用已经训练好的神经网络以节约训练和数据资源。如果我们在执行预测任务时，能够找到一个曾经执行过相似任务、并被训练得很好的大型架构，那我们就可以使用这个大型架构中位置较浅的那些层来帮助我们构筑自己的网络。借用已经训练好的模型来构筑新架构的技术就叫做预训练（pretrain）。具体而言，我们可以直接借用训练好的模型上的权重来初始化我们的模型。这里，我们使用ImageNet数据集预训练的网络参数初始化ResNet34。\n",
    "\n",
    "我们继续定义训练时需要用到的优化器和损失函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义优化器\n",
    "optimizer = optim.Adam(model.parameters(), lr=1e-4)\n",
    "# 定义损失函数\n",
    "criterion = nn.CrossEntropyLoss()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "关于优化器的相关知识请读者参阅《动手学深度学习》。我们使用常见的交叉熵损失来计算模型预测的标签与真实标签的误差，其定义如下：\n",
    "\n",
    "$$\\ell = -\\frac{1}{N} \\sum_{n=1}^N \\log p(y^{(n)}|\\mathbf{I}^{(n)}) $$\n",
    "\n",
    "接下来，我们开始编写训练模型、验证模型以及测试模型准确率的函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义训练函数\n",
    "def fit(model, dataloader):\n",
    "\n",
    "    model.train()\n",
    "    \n",
    "    # 初始化loss以及模型准确率\n",
    "    running_loss = 0.0\n",
    "    running_correct = 0\n",
    "    \n",
    "    # 开始迭代\n",
    "    for i, data in enumerate(dataloader):\n",
    "        \n",
    "        # 准备好训练的图像和标签，每次传入的数据量为batch_size\n",
    "        x, y = data[0].to(device), data[1].to(device)\n",
    "        # 我们需要在开始进行反向传播之前将梯度设置为零，因为PyTorch会在随后的反向传递中累积梯度\n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        # 将数据传入模型中，获得输出的预测标签\n",
    "        outputs = model(x)\n",
    "        \n",
    "        # 将预测标签与真实标签计算损失\n",
    "        loss = criterion(outputs, y)\n",
    "        \n",
    "        # 记录当前损失\n",
    "        running_loss += loss.item()\n",
    "        \n",
    "        # 记录当前准确率\n",
    "        _, preds = torch.max(outputs.data, 1)\n",
    "        running_correct += torch.sum(preds == y)\n",
    "        \n",
    "        # 反向传播\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        \n",
    "\n",
    "    loss = running_loss / len(dataloader.dataset)\n",
    "    accuracy = running_correct / len(dataloader.dataset)\n",
    "\n",
    "    print(f\"Train Loss: {loss:.4f}, Train Acc: {accuracy:.4f}\")\n",
    "\n",
    "    return loss, accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义验证函数，我们可以通过验证集对模型的参数进行调整\n",
    "def validate(model, dataloader):\n",
    "\n",
    "    model.eval()\n",
    "    \n",
    "    # 初始化loss以及模型准确率\n",
    "    running_loss = 0.0\n",
    "    correct = 0\n",
    "    with torch.no_grad():\n",
    "        for i, data in enumerate(dataloader):\n",
    "            # 流程同训练\n",
    "            x, y = data[0].to(device), data[1].to(device)\n",
    "            outputs = model(x)\n",
    "            loss = criterion(outputs, y)\n",
    "\n",
    "            running_loss += loss.item()\n",
    "            _, preds = torch.max(outputs.data, 1)\n",
    "            correct += torch.sum(preds == y)\n",
    "            \n",
    "        loss = running_loss / len(dataloader.dataset)\n",
    "        accuracy = correct / len(dataloader.dataset)\n",
    "        print(f'Val Loss: {loss:.4f}, Val Acc: {accuracy:.4f}')\n",
    "\n",
    "        return loss, accuracy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义测试函数，评估模型的效果\n",
    "def test(model, dataloader):\n",
    "    correct = 0\n",
    "    total = 0\n",
    "    with torch.no_grad():\n",
    "        for data in dataloader:\n",
    "            x, y = data[0].to(device), data[1].to(device)\n",
    "            outputs = model(x)\n",
    "            _, predicted = torch.max(outputs.data, 1)\n",
    "            total += y.size(0)\n",
    "            correct += torch.sum(predicted == y)\n",
    "    return correct, total\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们开始训练模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Training on 5486 examples, validating on 1829 examples...\n",
      "\n",
      "Epoch 1 of 25\n",
      "Train Loss: 0.0024, Train Acc: 0.0840\n",
      "Val Loss: 0.0019, Val Acc: 0.3100\n",
      "\n",
      "Epoch 2 of 25\n",
      "Train Loss: 0.0017, Train Acc: 0.3828\n",
      "Val Loss: 0.0015, Val Acc: 0.4352\n",
      "\n",
      "Epoch 3 of 25\n",
      "Train Loss: 0.0014, Train Acc: 0.5314\n",
      "Val Loss: 0.0013, Val Acc: 0.5145\n",
      "\n",
      "Epoch 4 of 25\n",
      "Train Loss: 0.0012, Train Acc: 0.6469\n",
      "Val Loss: 0.0011, Val Acc: 0.5894\n",
      "\n",
      "Epoch 5 of 25\n",
      "Train Loss: 0.0009, Train Acc: 0.7211\n",
      "Val Loss: 0.0010, Val Acc: 0.6698\n",
      "\n",
      "Epoch 6 of 25\n",
      "Train Loss: 0.0008, Train Acc: 0.7913\n",
      "Val Loss: 0.0008, Val Acc: 0.7523\n",
      "\n",
      "Epoch 7 of 25\n",
      "Train Loss: 0.0006, Train Acc: 0.8482\n",
      "Val Loss: 0.0007, Val Acc: 0.7922\n",
      "\n",
      "Epoch 8 of 25\n",
      "Train Loss: 0.0005, Train Acc: 0.8941\n",
      "Val Loss: 0.0006, Val Acc: 0.8387\n",
      "\n",
      "Epoch 9 of 25\n",
      "Train Loss: 0.0004, Train Acc: 0.9306\n",
      "Val Loss: 0.0005, Val Acc: 0.8737\n",
      "\n",
      "Epoch 10 of 25\n",
      "Train Loss: 0.0003, Train Acc: 0.9564\n",
      "Val Loss: 0.0004, Val Acc: 0.8961\n",
      "\n",
      "Epoch 11 of 25\n",
      "Train Loss: 0.0002, Train Acc: 0.9750\n",
      "Val Loss: 0.0004, Val Acc: 0.9103\n",
      "\n",
      "Epoch 12 of 25\n",
      "Train Loss: 0.0002, Train Acc: 0.9869\n",
      "Val Loss: 0.0003, Val Acc: 0.9202\n",
      "\n",
      "Epoch 13 of 25\n",
      "Train Loss: 0.0001, Train Acc: 0.9933\n",
      "Val Loss: 0.0003, Val Acc: 0.9328\n",
      "\n",
      "Epoch 14 of 25\n",
      "Train Loss: 0.0001, Train Acc: 0.9958\n",
      "Val Loss: 0.0003, Val Acc: 0.9311\n",
      "\n",
      "Epoch 15 of 25\n",
      "Train Loss: 0.0001, Train Acc: 0.9973\n",
      "Val Loss: 0.0002, Val Acc: 0.9355\n",
      "\n",
      "Epoch 16 of 25\n",
      "Train Loss: 0.0001, Train Acc: 0.9985\n",
      "Val Loss: 0.0002, Val Acc: 0.9360\n",
      "\n",
      "Epoch 17 of 25\n",
      "Train Loss: 0.0000, Train Acc: 0.9989\n",
      "Val Loss: 0.0002, Val Acc: 0.9360\n",
      "\n",
      "Epoch 18 of 25\n",
      "Train Loss: 0.0000, Train Acc: 0.9987\n",
      "Val Loss: 0.0002, Val Acc: 0.9377\n",
      "\n",
      "Epoch 19 of 25\n",
      "Train Loss: 0.0000, Train Acc: 0.9993\n",
      "Val Loss: 0.0002, Val Acc: 0.9399\n",
      "\n",
      "Epoch 20 of 25\n",
      "Train Loss: 0.0000, Train Acc: 0.9996\n",
      "Val Loss: 0.0002, Val Acc: 0.9410\n",
      "\n",
      "Epoch 21 of 25\n",
      "Train Loss: 0.0000, Train Acc: 0.9996\n",
      "Val Loss: 0.0002, Val Acc: 0.9404\n",
      "\n",
      "Epoch 22 of 25\n",
      "Train Loss: 0.0000, Train Acc: 0.9998\n",
      "Val Loss: 0.0002, Val Acc: 0.9415\n",
      "\n",
      "Epoch 23 of 25\n",
      "Train Loss: 0.0000, Train Acc: 0.9998\n",
      "Val Loss: 0.0002, Val Acc: 0.9426\n",
      "\n",
      "Epoch 24 of 25\n",
      "Train Loss: 0.0000, Train Acc: 0.9998\n",
      "Val Loss: 0.0002, Val Acc: 0.9420\n",
      "\n",
      "Epoch 25 of 25\n",
      "Train Loss: 0.0000, Train Acc: 1.0000\n",
      "Val Loss: 0.0002, Val Acc: 0.9420\n"
     ]
    }
   ],
   "source": [
    "# 开始对模型进行训练\n",
    "\n",
    "# 设定迭代的轮次\n",
    "epochs = 25\n",
    "# 设定训练以及验证的损失与准确率\n",
    "train_loss , train_accuracy = [], []\n",
    "val_loss , val_accuracy = [], []\n",
    "\n",
    "print(f\"Training on {len(train_data)} examples, validating on {len(val_data)} examples...\")\n",
    "\n",
    "# 开始迭代\n",
    "for epoch in range(epochs):\n",
    "    # 输出迭代信息\n",
    "    print(f\"\\nEpoch {epoch+1} of {epochs}\")\n",
    "    train_epoch_loss, train_epoch_accuracy = fit(model, trainloader)\n",
    "    val_epoch_loss, val_epoch_accuracy = validate(model, valloader)\n",
    "    train_loss.append(train_epoch_loss)\n",
    "    train_accuracy.append(train_epoch_accuracy.cpu())\n",
    "    val_loss.append(val_epoch_loss)\n",
    "    val_accuracy.append(val_epoch_accuracy.cpu())\n",
    "    \n",
    "# 可以保存训练的模型\n",
    "# torch.save(model.state_dict(), f\"../outputs/models/resnet34_epochs{epochs}.pth\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们绘制并观察准确率和损失曲线。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.legend.Legend at 0x7ff9ae7ba350>"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1EAAAJaCAYAAADONc3dAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/YYfK9AAAACXBIWXMAAA9hAAAPYQGoP6dpAAB6BElEQVR4nO3dd3gUZdvG4WtTSUISekgg9CIdpYkIqKAgrxSliShVVAQEEQUsgK8FVDoiKErvICAq4qsgFqRICSBNqqD0lkBC6s73x3wJBAJkk01mN/mdx7EHs7Ozz9wJS8zl88w9NsMwDAEAAAAA0sXD6gIAAAAAwJ0QogAAAADAAYQoAAAAAHAAIQoAAAAAHECIAgAAAAAHEKIAAAAAwAGEKAAAAABwACEKAAAAABzgZXUB2c1ut+vEiRMKDAyUzWazuhwAAAAAFjEMQ5cvX1ZYWJg8PNI/v5TrQtSJEycUHh5udRkAAAAAXMTx48dVvHjxdB+f60JUYGCgJPMbFRQUZHE1AAAAAKwSFRWl8PDwlIyQXrkuRCUv4QsKCiJEAQAAAHD4Mh8aSwAAAACAAwhRAAAAAOAAQhQAAAAAOCDXXROVHoZhKDExUUlJSVaXAmQ7T09PeXl5cQsAAACAWyBE3SA+Pl4nT55UTEyM1aUAlvH391doaKh8fHysLgUAAMDlEKKuY7fbdeTIEXl6eiosLEw+Pj7833jkKoZhKD4+XmfPntWRI0dUvnx5h248BwAAkBsQoq4THx8vu92u8PBw+fv7W10OYAk/Pz95e3vr77//Vnx8vPLkyWN1SQAAAC6F/8WcBv7PO3I7/g0AAADcGr8pAQAAAIADCFFIU6lSpTR+/HirywAAAABcDtdE5RAPPPCAatas6bTg88cffyggIMApYwEAAAA5CSEqFzEMQ0lJSfLyuvNfe+HChbOhouzlyNcPAAAA3ArL+XKAbt266eeff9aECRNks9lks9l09OhRrVu3TjabTd99951q1aolX19f/fbbbzp06JBat26tkJAQ5c2bV3Xq1NGPP/6Yaswbl/PZbDZ9/vnnevzxx+Xv76/y5ctr5cqVt61rzpw5ql27tgIDA1W0aFE99dRTOnPmTKpjdu/erccee0xBQUEKDAxUw4YNdejQoZTXp0+fripVqsjX11ehoaHq27evJOno0aOy2WyKiIhIOfbSpUuy2Wxat26dJGXq64+Li9PgwYMVHh4uX19flStXTl988YUMw1C5cuU0evToVMdHRETIZrPp4MGDt/2eAAAAwP0Rou7AMAxFx0db8jAMI101TpgwQfXr11evXr108uRJnTx5UuHh4SmvDxkyRKNGjdLevXtVvXp1XblyRS1atNCaNWu0fft2NW/eXC1bttSxY8due563335bHTp00M6dO9WiRQt17txZFy5cuOXxCQkJeuedd7Rjxw6tWLFCR48eVbdu3VJe//fff9WoUSP5+vpq7dq12rp1q3r06KHExERJ0pQpU9SnTx8999xz2rVrl1auXKly5cql63tyvYx8/V26dNGCBQs0ceJE7d27V59++qny5s0rm82mHj16aMaMGanOMWPGDDVq1ChD9QEAAMC9sK7pDmISYpR3ZF5Lzn1l6BUF+Nz5uqTg4GD5+PjI399fRYsWven1//73v3r44YdTnhcoUEA1atRIef7OO+9o+fLlWrlyZcpMT1q6deumTp06SZLef/99TZw4UZs3b1bz5s3TPL5Hjx4p22XKlNHEiRNVp04dXblyRXnz5tXkyZMVHByshQsXytvbW5JUoUKFlPe8++67euWVV9S/f/+UfXXq1LnTt+Mmjn79f/31lxYvXqwffvhBTZs2Tan/+u/DsGHDtHnzZtWtW1cJCQmaP3/+TbNTAAAAyJmYicoFateuner5lStXNGjQIFWqVEn58uVT3rx5tXfv3jvORFWvXj1lOyAgQEFBQTctz7ve1q1b1bJlS5UoUUKBgYFq3LixJKWcJyIiQg0bNkwJUNc7c+aMTpw4oSZNmqT767wVR7/+iIgIeXp6ptR7o7CwMP3nP//R9OnTJUlff/214uLi1L59+0zXCgAAANfHTNQd+Hv768rQK5ad2xlu7LI3aNAg/fDDDxo9erTKlSsnPz8/tWvXTvHx8bcd58awY7PZZLfb0zw2OjpazZo1U7NmzTRv3jwVLlxYx44dU7NmzVLO4+fnd8tz3e416drNYK9f8piQkJDmsY5+/Xc6tyQ9++yzeuaZZzRu3DjNmDFDHTt2lL+/c/6+AAAA4NosnYn65Zdf1LJlS4WFhclms2nFihV3fM+6det0zz33pFzsP3PmzCyt0WazKcAnwJKHzWZLd50+Pj5KSkpK17Hr169Xt27d9Pjjj6tatWoqWrSojh49msHvUNr27dun8+fPa9SoUWrYsKHuuuuum2atqlevrl9//TXN8BMYGKhSpUppzZo1aY6f3D3w5MmTKfuubzJxO3f6+qtVqya73a6ff/75lmO0aNFCAQEBmjJlilavXp1q6SIAAAByNktDVHR0tGrUqKHJkyen6/gjR47oP//5jx588EFFRERowIABevbZZ/X9999ncaWur1SpUtq0aZOOHj2qc+fO3XKGSJLKly+vZcuWKSIiQjt27NBTTz112+MzokSJEvLx8dGkSZN0+PBhrVy5Uu+8806qY/r27auoqCg9+eST2rJliw4cOKA5c+Zo//79kqQRI0ZozJgxmjhxog4cOKBt27Zp0qRJkszZonvvvTelYcTPP/+sN998M1213enrL1WqlLp27aoePXpoxYoVOnLkiNatW6fFixenHOPp6alu3bpp6NChKl++vOrXr5/ZbxkAAADchKUh6tFHH9W7776rxx9/PF3HT506VaVLl9aYMWNUqVIl9e3bV+3atdO4ceOyuFLXN2jQIHl6eqpy5copS+duZezYscqfP7/uu+8+tWzZUs2aNdM999zj1HoKFy6smTNnasmSJapcubJGjRp1U+OFggULau3atbpy5YoaN26sWrVqadq0aSnLBrt27arx48frk08+UZUqVfTYY4/pwIEDKe+fPn26EhMTVatWLQ0YMEDvvvtuumpLz9c/ZcoUtWvXTi+++KLuuusu9erVS9HR0amO6dmzp+Lj49W9e/eMfIsAAADgpmxGevtoZzGbzably5erTZs2tzymUaNGuueee1Ldv2jGjBkaMGCAIiMj03xPXFyc4uLiUp5HRUUpPDxckZGRCgoKSnVsbGysjhw5otKlSytPnjyZ+nqQ8/36669q0qSJjh8/rpCQEKvLcSr+LQDIjCR7kuKT4pVkJMkwDNkNe8rD0LXn6X3t+v23e80wDBkyf62xybxvYvKfae3LzGuSUu2zG3Yl2ZPMP42klOfXb9/4WvLz2712p3Ey8n260/fwln8nMtJ9+xVnSj5v8t9t8vb1f954XGZfy03u9L3NytfWdV2ngv4FLfiqr4mKilJwcHCa2eB23KqxxKlTp276ZTUkJERRUVG6evVqmg0BRo4cqbfffju7SkQuEBcXp7Nnz2rEiBFq3759jgtQAFxboj1R8UnxikuMU1xSXMqfae2LS/z//f//+o2PhKSENPfH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aPciLd+7XJFxkSmvhQeFq0OVDupQpYPqhNXhJrUAANwBIeoO/P2lGy6JydZzp1fLli1lGIa+/fZb1alTR7/++qvGjRuX8vqgQYP0ww8/aPTo0SpXrpz8/PzUrl07xcfHO63ehQsXatCgQRozZozq16+vwMBAffTRR9q0adNt37dhwwZ17txZb7/9tpo1a6bg4GAtXLhQY8aMyXRN3t7XbsiZ/IvhjdeOJTt69Kgee+wx9e7dW++9954KFCig3377TT179lR8fLz8/f3l5+d3y3Pd7jVJKYHWMIyUfddf93X9ODf+Etu1a1edP39eEyZMUMmSJeXr66v69eun/P3d6dyS9Oyzz2ry5MkaMmSIZsyYoe7du/PLcg6XaE/UuqPrtOjPRVq2b5kuXL2Q8lpo3lC1r9xeHat21L3F76XVOAAADiBE3YHNJgUEWF3FneXJk0dPPPGE5s2bp4MHD6pixYq65557Ul5fv369unXrpscff1ySOTOV3MwgPSpVqqQ5c+YoNjY2ZTZq48aNqY5Zv3697rvvPr344osp+w4dOpTqGB8fn5Trh5L9/vvvKlmypN54442UfX//fXMHsGRBQUEKCwvT+vXr1bhx41Tnr1u3brq/phtt3bpVdrtdY8aMSQk8ixcvTnVM9erVtWbNGr399ts3vb98+fLy8/PTmjVr9Oyzz970euHChSWlbh2f3vterV+/Xp988olatGghSTp+/LjOnTuXqq5//vlHf/311y1no55++mm99tprmjhxovbs2aOuXbum69xwL0n2JP167Fct3r1YS/cs1dmYsymvFQkoonaV2qlj1Y66v8T9BCcAADKIEJWDdO7cWY899ph2796tp59+OtVr5cuX17Jly9SyZUvZbDa99dZbt5yRSctTTz2lN954Q7169dLQoUN19OhRjR49+qZzzJ49W99//71Kly6tOXPm6I8//lDp0qVTjilVqpS+//577d+/XwULFlRwcLDKly+vY8eOaeHChapTp46+/fZbLV++/Lb1vPrqqxo+fLjKli2rmjVrasaMGYqIiNC8efPS/TXdqFy5ckpISNCkSZPUsmVLrV+/XlOnTk11zNChQ1WtWjW9+OKLeuGFF+Tj46OffvpJ7du3V6FChTR48GC99tpr8vHxUYMGDXT27Fnt3r1bPXv2VLly5RQeHq4RI0bovffe019//ZXu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V0KIgs6fl3r0MLdfeklq0iT169Hx0Vq6Z6kkqUuNLtlcHQAAAOBaCFG5nGFIvXtLJ09Kd90ljRp18zHL9i5TdEK0yuQvowbhDbK/SAAAAMCFEKJyufnzpSVLJC8vae5cyS+NlXqzd5pd+bpU78K9oQAAAJDrEaJysePHpT59zO1hw6RatdI4JvK41hxeI4mlfAAAAIBEiMq17Hape3cpMlKqV08aOjTt4+btmidDhhqVbKTS+Utnb5EAAACACyJE5VIffyytWWMu35s921zOdyPDMFK68nWpziwUAAAAIBGicqW9e6XBg83tMWOkChXSPu6PE39o37l9yuOVR+2rtM++AgEAAAAXRojKZRITpWeekWJjpebNpRdeuPWxs3eYDSUev+txBfkGZVOFAAAAgGsjROUyX34pbd0q5c8vffGFdKtme3GJcVrw5wJJUtcaXbOxQgAAAMC1EaJymfHjzT/795fCwm593KoDq3Th6gWF5g1V0zJNs6U2AAAAwB0QonKRjRvNh4+PeYPd20luKPF09afl6eGZDdUBAAAA7oEQlYskz0J17iwVKXLr487FnNO3B76VxL2hAAAAgBsRonKJ48elpUvN7f79b3/sgl0LlGhP1D2h96hqkapZXxwAAADgRghRucTkyVJSkvTgg1KNGrc/dvZOsysfDSUAAACAmxGicoHoaOmzz8ztAQNuf+yes3u05cQWeXl4qVPVTlleGwAAAOBuCFG5wOzZ0sWLUtmy0mOP3f7YWRFmQ4kW5VuocEDhbKgOAAAAcC+EqBzObpcmTDC3+/eXPG7zN55kT9LcXXMlSV2q01ACAAAASAshKof7/ntp/34pKEjq1u32x645skYnLp9Q/jz59ViFO0xZAQAAALkUISqHS25r/uyzUmDg7Y+dvcNsKPFk1Sfl6+WbtYUBAAAAbooQlYPt3i3973/mEr5+/W5/bFRclJbtXSaJrnwAAADA7RCicrDka6Eef1wqVer2x36550tdTbyqigUrqm6xulleGwAAAOCuCFE51Llz0pw55vad2ppL0qwdZle+LjW6yGazZV1hAAAAgJsjROVQn30mxcZKtWpJDRrc/tgjF4/o579/lk02PV396ewpEAAAAHBTLhGiJk+erFKlSilPnjyqV6+eNm/enK73LVy4UDabTW3atMnaAt1MfLw0ebK5PWCAdKeJpbk7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rrVr16p06dKpXq9Vq5a8vb1T/Vzav3+/jh07xs8lpHKnz1Jakht0OfJzieV8Fhk4cKC6du2q2rVrq27duho/fryio6PVvXt3q0uDGxk0aJBatmypkiVL6sSJExo+fLg8PT3VqVMnq0uDi7ty5Uqq/+N25MgRRUREqECBAipRooQGDBigd999V+XLl1fp0qX11ltvKSwsLFXXNUC6/WepQIECevvtt9W2bVsVLVpUhw4d0muvvaZy5cqpWbNmFlYNV9OnTx/Nnz9fX331lQIDA1OucwoODpafn5+Cg4PVs2dPDRw4UAUKFFBQUJD69eun+vXr05kPqdzps3To0CHNnz9fLVq0UMGCBbVz5069/PLLatSokapXr57+ExmwzKRJk4wSJUoYPj4+Rt26dY2NGzdaXRLcTMeOHY3Q0FDDx8fHKFasmNGxY0fj4MGDVpcFN/DTTz8Zkm56dO3a1TAMw7Db7cZbb71lhISEGL6+vkaTJk2M/fv3W1s0XNLtPksxMTHGI488YhQuXNjw9vY2SpYsafTq1cs4deqU1WXDxaT1GZJkzJgxI+WYq1evGi+++KKRP39+w9/f33j88ceNkydPWlc0XNKdPkvHjh0zGjVqZBQoUMDw9fU1ypUrZ7z66qtGZGSkQ+fhPlEAAAAA4ACuiQIAAAAABxCiAAAAAMABhCgAAAAAcAAhCgAAAAAcQIgCAAAAAAcQogAAAADAAYQoAAAAAHAAIQoAgNuw2WxasWKF1WUAAFwIIQoA4LK6desmm81206N58+ZWlwYAyMW8rC4AAIDbad68uWbMmJFqn6+vr0XVAADATBQAwMX5+vqqaNGiqR758+eXZC61mzJlih599FH5+fmpTJkyWrp0aar379q1Sw899JD8/PxUsGBBPffcc7py5UqqY6ZPn64qVarI19dXoaGh6tu3b6rXz507p8cff1z+/v4qX768Vq5cmfLaxYsX1blzZxUuXFh+fn4qX778TaEPAJCzEKIAAG7trbfeUtu2bbVjxw517txZTz75pPbu3StJio6OVrNmzZQ/f3798ccfWrJkiX788cdUIWnKlCnq06ePnnvuOe3atUsrV65UuXLlUp3j7bffVocOHbRz5061aNFCnTt31oULF1LOv2fPHn333Xfau3evpkyZokKFCmXfNwAAkO1shmEYVhcBAEBaunXrprlz5ypPnjyp9r/++ut6/fXXZbPZ9MILL2jKlCkpr917772655579Mknn2jatGkaPHiwjh8/roCAAEnSqlWr1LJlS504cUIhISEqVqyYunfvrnfffTfNGmw2m95880298847ksxgljdvXn333Xdq3ry5WrVqpUKFCmn69OlZ9F0AALgarokCALi0Bx98MFVIkqQCBQqkbNevXz/Va/Xr11dERIQkae/evapRo0ZKgJKkBg0ayG63a//+/bLZbDpx4oSaNGly2xqqV6+esh0QEKCgoCCdOXNGktS7d2+1bdtW27Zt0yOPPKI2bdrovvvuy9DXCgBwD4QoAIBLCwgIuGl5nbP4+fml6zhvb+9Uz202m+x2uyTp0Ucf1d9//61Vq1bphx9+UJMmTdSnTx+NHj3a6fUCAFwD10QBANzaxo0bb3peqVIlSVKlSpW0Y8cORUdHp7y+fv16eXh4qGLFigoMDFSpUqW0Zs2aTNVQuHBhde3aVXPnztX48eP12WefZWo8AIBrYyYKAODS4uLidOrUqVT7vLy8Upo3LFmyRLVr19b999+vefPmafPmzfriiy8kSZ07d9bw4cPVtWtXjRgxQmfPnlW/fv30zDPPKCQkRJI0YsQIvfDCCypSpIgeffRRXb58WevXr1e/fv3SVd+wYcNUq1YtValSRXFxcfrmm29SQhwAIGciRAEAXNrq1asVGhqaal/FihW1b98+SWbnvIULF+rFF19UaGioFixYoMqVK0uS/P399f3336t///6qU6eO/P391bZtW40dOzZlrK5duyo2Nlbjxo3ToEGDVKhQIbVr1y7d9fn4+Gjo0KE6evSo/Pz81LBhQy1cuNAJXzkAwFXRnQ8A4LZsNpuWL1+uNm3aWF0KACAX4ZooAAAAAHAAIQoAAAAAHMA1UQAAt8WKdACAFZiJAgAAAAAHEKIAAAAAwAGEKAAAAABwACEKAAAAABxAiAIAAAAABxCiAAAAAMABhCgAAAAAcAAhCgAAAAAcQIgCAAAAAAf8H6VC8h9aRkmsAAAAAElFTkSuQmCC",
      "text/plain": [
       "<Figure size 1000x700 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "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BgwYkLd/xIgRvPjii9x555188MEHzJ49mw4dOrB169ZS+1U9++yzjBgxgssuu4w333yT2bNnM2jQoP1+xvTp0xkwYACnn346r776Kl988QU333xzqdS2622HO29P3P3ZvgPx5JNPMn36dHr27MnEiRNp1aoVn3zyCRA8Z/fNN99wxhln8Pbbb3P44Yfz4osvltpn74vPrFU1O5uMbFgYNBnxuTVJklQVhEKwy5WVaJWYmMi5557LM888w8KFC2ndujVHHnlk3v6PPvqISy+9lHPOOQcIrrQtWbKkyOdv27YtTz/9NJs3b867urYzlOz6GT179uTKK6/M27b71aT4+HhycnIKbPv4449p1qwZN998c962nU06CpOUlERaWhofffQRxx9/fIHP7969e5F/pv1p27Yt48ePJzs7O+/q2kcffURMTAytW7fOG9e5c2c6d+7MjTfeSI8ePZgwYQJHHXUUAK1ataJVq1Zce+219O/fnyeffDLvf4Oy5JW1qqh+1+DVWyElSZKizoABA3jttdd44oknClxVA2jZsiUvvPACs2fP5ssvv+Siiy4q1hWmiy66iFAoxOWXX87cuXOZPHky99133x6f8dlnn/HGG2/w7bffMnLkSD799NMCY5o3b85XX33FggULWLt2Ldu2baNly5YsW7aMZ599lkWLFvHQQw/t9wrUH//4R+6++24mTpzIggULuOGGG5g9e/YeTU8OxIABA0hMTOSSSy7h66+/5p133mHYsGFcfPHFpKSksHjxYm688UamT5/O0qVLefPNN/nuu+9o27YtmzZtYujQobz77rssXbqUjz76iE8//ZS2bduWWn37YlirivKeWyuflqOSJEkqul/96lfUq1ePBQsWcNFFFxXYd//991O3bl169uzJmWeeSe/evQtcedufWrVq8b///Y85c+bQuXNnbr75Zu6+++4CY37/+99z7rnn0q9fP9LT01m3bl2Bq2wAl19+Oa1bt6Zr1640bNiQjz76iLPOOotrr72WoUOH0qlTJz7++GNGjhy5z3quuuoqhg8fznXXXUeHDh2YMmUKr7zySoFOkAeqRo0avPHGG/z0009069aN8847j5NOOomHH344b//8+fPp27cvrVq1YvDgwQwZMoTf//73xMbGsm7dOgYOHEirVq244IILOO2007jttttKrb59Ce3olqIykJWVRXJyMpmZmSQlJUW6nHyr3oFpv4KazeHsxZGuRpIkqVRt3ryZxYsX06JFiwKNNKSytq/vXkmygVfWqiKbjEiSJElRz7BWFe1sMgJBkxFJkiRJUcewVlXZZESSJEmKaoa1qsomI5IkSVJUM6xVVXlhzStrkiRJUjQyrFVVNhmRJEmVnE3PVd5K+ztnWKuqbDIiSZIqqWrVqgGwcePGCFeiqmbnd27nd/BAxZXKWVQx1esCGxYGt0I2PjnS1UiSJJWK2NhY6tSpw+rVq4Fg0uNQKBThqlSZhcNhNm7cyOrVq6lTpw6xsbGlcl7DWlVWvyssm2iTEUmSVOmkpqYC5AU2qTzUqVMn77tXGgxrVZlNRiRJUiUVCoVo3LgxjRo1Ytu2bZEuR1VAtWrVSu2K2k6Gtaps9yYjCfUjWo4kSVJpi42NLfV/QEvlxQYjVZlNRiRJkqSoZVir6rwVUpIkSYpKhrWqrn7X4NUmI5IkSVJUMaxVdV5ZkyRJkqKSYa2q273JiCRJkqSoYFir6mwyIkmSJEUlw5q8FVKSJEmKQoY12WREkiRJikKGNXllTZIkSYpChjXZZESSJEmKQoY12WREkiRJikKGNQW8FVKSJEmKKoY1BWwyIkmSJEUVw5oCXlmTJEmSoophTQGbjEiSJElRxbCmgE1GJEmSpKhiWFM+b4WUJEmSooZhTfnywppNRiRJkqRIM6wpX15HSK+sSZIkSZFmWFM+m4xIkiRJUcOwpnw2GZEkSZKihmFNBdlkRJIkSYoKhjUVZJMRSZIkKSoY1lSQTUYkSZKkqGBYU0E2GZEkSZKigmFNBdlkRJIkSYoKhjXtySYjkiRJUsQZ1rQnm4xIkiRJEWdY055sMiJJkiRFnGFNe7LJiCRJkhRxhjXtySYjkiRJUsQZ1lQ4m4xIkiRJEWVYU+FsMiJJkiRFlGFNhbPJiCRJkhRRhjUVziYjkiRJUkQZ1lQ4m4xIkiRJEWVY097ZZESSJEmKGMOa9s4mI5IkSVLEGNa0dzYZkSRJkiLGsKa9s8mIJEmSFDGGNe2dTUYkSZKkiDGsad9sMiJJkiRFhGFN+2ZYkyRJkiLCsKZ9syOkJEmSFBGGNe1bPZuMSJIkSZFgWNO+xdexyYgkSZIUAYY17Z/PrUmSJEnlzrCm/TOsSZIkSeXOsKb9s8mIJEmSVO4Ma9o/m4xIkiRJ5c6wpv2zyYgkSZJU7gxrKhqfW5MkSZLKVVSEtUceeYTmzZuTmJhIeno6M2fO3Of4SZMm0aZNGxITE+nQoQOTJ08usD8cDjNq1CgaN25M9erV6dWrF999913e/iVLlnDZZZfRokULqlevzqGHHsqtt97K1q1bC5znq6++4thjjyUxMZGmTZtyzz33lN4PXdEY1iRJkqRyFfGwNnHiRIYPH86tt97KrFmz6NixI71792b16tWFjv/444/p378/l112GV988QV9+vShT58+fP3113lj7rnnHh566CHGjRvHjBkzqFmzJr1792bz5s0AzJ8/n9zcXP7xj3/wzTff8Le//Y1x48Zx00035Z0jKyuLU045hWbNmvH5559z7733Mnr0aB577LGy/YVEK5uMSJIkSeUqFA6Hw5EsID09nW7duvHwww8DkJubS9OmTRk2bBg33HDDHuP79etHdnY2r776at62o446ik6dOjFu3DjC4TBpaWlcd911jBgxAoDMzExSUlIYP348F154YaF13HvvvTz66KN8//33ADz66KPcfPPNZGRkEB8fD8ANN9zASy+9xPz584v0s2VlZZGcnExmZiZJSUlF/6VEo63r4fm6wXrftZBQP6LlSJIkSRVJSbJBRK+sbd26lc8//5xevXrlbYuJiaFXr15Mnz690GOmT59eYDxA796988YvXryYjIyMAmOSk5NJT0/f6zkhCHT16tUr8DnHHXdcXlDb+TkLFizg559/LvQcW7ZsISsrq8BSadhkRJIkSSpXEQ1ra9euJScnh5SUlALbU1JSyMjIKPSYjIyMfY7f+Vqccy5cuJCxY8fy+9//fr+fs+tn7O6uu+4iOTk5b2natGmh4yosn1uTJEmSyk3En1mLtB9//JFTTz2V888/n8svv/yAznXjjTeSmZmZtyxfvryUqowShjVJkiSp3EQ0rDVo0IDY2FhWrVpVYPuqVatITU0t9JjU1NR9jt/5WpRzrlixghNPPJGePXvu0Thkb5+z62fsLiEhgaSkpAJLpWKTEUmSJKncRDSsxcfH06VLF6ZNm5a3LTc3l2nTptGjR49Cj+nRo0eB8QBTp07NG9+iRQtSU1MLjMnKymLGjBkFzvnjjz9ywgkn0KVLF5588kliYgr+Knr06MH777/Ptm3bCnxO69atqVu3bsl/6Iqs3pHBa/YS2LIuoqVIkiRJ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",
      "text/plain": [
       "<Figure size 1000x700 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 绘制准确率曲线\n",
    "plt.figure(figsize=(10, 7))\n",
    "# 训练集的准确率曲线\n",
    "plt.plot(train_accuracy, color='green', label='train accuracy')\n",
    "# 验证集准确率曲线\n",
    "plt.plot(val_accuracy, color='blue', label='validataion accuracy')\n",
    "plt.xlabel('Epochs')\n",
    "plt.ylabel('Accuracy')\n",
    "plt.legend()\n",
    "\n",
    "# 绘制loss曲线\n",
    "plt.figure(figsize=(10, 7))\n",
    "plt.plot(train_loss, color='orange', label='train loss')\n",
    "plt.plot(val_loss, color='red', label='validataion loss')\n",
    "plt.xlabel('Epochs')\n",
    "plt.ylabel('Loss')\n",
    "plt.legend()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后，我们对模型的准确率进行评估。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy of the network on test images: 93.111 %\n"
     ]
    }
   ],
   "source": [
    "correct, total = test(model, testloader)\n",
    "print('Accuracy of the network on test images: %0.3f %%' % (100 * correct / total))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 10.5 小结"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在这一章中，我们学习了视觉识别中最基础的问题——图像分类。我们先后介绍了基于手工设计图像表征（即视觉词袋模型）的图像分类算法和基于深度卷积神经网络的图像分类算法。在后续的章节中，我们将介绍如何把基于深度卷积神经网络的图像分类算法推广到其他的视觉识别问题中。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 10.6 习题"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 习题10.1：词袋模型中，视觉词典的大小，即视觉单词的个数由超参$K$决定。如果$K$设置得过小或者过大，分别对词袋模型的图像表征能力有何影响？\n",
    "\n",
    "#### 习题10.2：在我们介绍的词袋模型中，对每个图像特征的编码我们采用的是最近邻编码，即将这个特征分配给视觉词典中与其最相似的一个视觉单词。我们也可以采用其他的编码策略，比如我们可以这个特征分配给视觉词典中与其最相似$N$个视觉单词，分配的权重由它们之间的相似度确定。请你动手实现基于这样一个编码策略的词袋模型。\n",
    "\n",
    "#### 习题10.3：在基于深度卷积神经网络的图像分类算法中，我们对图像数据做了很多增强。请你通过实验验证数据增强对分类结果的影响。\n",
    "\n",
    "#### 习题10.4：在基于深度卷积神经网络的图像分类算法中，我们对网络模型进行了预训练。请你通过实验验证预训练对分类结果的影响。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 参考文献"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[1] Svetlana Lazebnik, Cordelia Schmid, Jean Ponce: Beyond Bags of Features: Spatial Pyramid Matching for Recognizing Natural Scene Categories. CVPR (2) 2006: 2169-2178\n",
    "\n",
    "[2] Josef Sivic, Andrew Zisserman: Video Google: A Text Retrieval Approach to Object Matching in Videos. ICCV 2003: 1470-1477\n",
    "\n",
    "[3] Stephen O'Hara, Bruce A. Draper: Introduction to the Bag of Features Paradigm for Image Classification and Retrieval. CoRR abs/1101.3354 (2011)\n",
    "\n",
    "[4] Tsai C F. Bag-of-words representation in image annotation: A review. International Scholarly Research Notices, 2012\n",
    "\n",
    "[5] Yann LeCun, Bernhard E. Boser, John S. Denker, Donnie Henderson, Richard E. Howard, Wayne E. Hubbard, Lawrence D. Jackel:\n",
    "Backpropagation Applied to Handwritten Zip Code Recognition. Neural Comput. 1(4): 541-551 (1989)\n",
    "\n",
    "[6] Yann LeCun, Léon Bottou, Yoshua Bengio, Patrick Haffner: Gradient-based learning applied to document recognition. Proc. IEEE 86(11): 2278-2324 (1998)\n",
    "\n",
    "[7] Alex Krizhevsky, Ilya Sutskever, Geoffrey E. Hinton: ImageNet Classification with Deep Convolutional Neural Networks. NIPS 2012: 1106-1114\n",
    "\n",
    "[8] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun: Deep Residual Learning for Image Recognition. CVPR 2016: 770-778\n",
    "\n",
    "[9]Yann LeCun. The MNIST database of handwritten digits[J]. http://yann.lecun.com/exdb/mnist/, 1998.\n",
    "\n",
    "[10] Li Fei-Fei, Rob Fergus, Pietro Perona: Learning Generative Visual Models from Few Training Examples: An Incremental Bayesian Approach Tested on 101 Object Categories. CVPR Workshops 2004: 178\n",
    "\n",
    "[11] Krizhevsky A, Hinton G. Learning multiple layers of features from tiny images. 2009.\n",
    "\n",
    "[12] Jia Deng, Wei Dong, Richard Socher, Li-Jia Li, Kai Li, Li Fei-Fei: ImageNet: A large-scale hierarchical image database. CVPR 2009: 248-255\n",
    "\n",
    "[13] C. Liu, J. Yuen, A. Torralba, J. Sivic, W. Freeman, Sift flow: dense correspondence across different scenes, in: Proceedings of 10th European Conference on Computer Vision, 2008, pp. 28–42.\n",
    "\n",
    "[14] C. Liu, J. Yuen, A. Torralba, Sift flow: dense correspondence across scenes and its applications\n",
    "IEEE Trans. Pattern Anal. Mach. Intell., 33 (5) (2011), pp. 978-994"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.7"
  },
  "vscode": {
   "interpreter": {
    "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
